package com.adingxiong.security.springsecurity.config;

import com.adingxiong.security.springsecurity.config.component.*;
import com.adingxiong.security.springsecurity.filter.SmsCodeFilter;
import com.adingxiong.security.springsecurity.filter.ValidateCodeFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;


/**
 * @ClassName BrowserSecurityConfig
 * @Description   密码再控制台  用户名为user
 *
 *
 *      拦截书序  ，Spring Security包含了众多的过滤器，这些过滤器形成了一条链，
 *      所有请求都必须通过这些过滤器后才能成功访问到资源。其中UsernamePasswordAuthenticationFilter过滤器用于处理基于表单方式的登录认证，
 *      而BasicAuthenticationFilter用于处理基于HTTP Basic方式的登录验证，后面还可能包含一系列别的过滤器
 *      （可以通过相应配置开启）。在过滤器链的末尾是一个名为FilterSecurityInterceptor的拦截器，用于判断当前请求身份认证是否成功，
 *      是否有相应的权限，当身份认证失败或者权限不足的时候便会抛出相应的异常。ExceptionTranslateFilter捕获并处理，
 *      所以我们在ExceptionTranslateFilter过滤器用于处理了FilterSecurityInterceptor抛出的异常并进行处理，
 *      比如需要身份认证时将请求重定向到相应的认证页面，当认证失败或者权限不足时返回相应的提示信息。
 * @Author xiongchao
 * @Date 2020/10/15 17:35
 **/
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)  //开启代码权限控制注解
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private ValidateCodeFilter validateCodeFilter;

    @Autowired
    private SmsCodeFilter smsCodeFilter ;


    @Autowired
    private DataSource dataSource;

    @Autowired
    private SmsAuthenticationConfig smsAuthenticationConfig;

    @Bean
    public PersistentTokenRepository repository(){
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        //自动创建持久化token  第二次设置成false  不然会报错  表已经存在创建失败
        jdbcTokenRepository.setCreateTableOnStartup(false);
        return jdbcTokenRepository;
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        //默认是 表单方式  formLogin
        httpSecurity.addFilterBefore(validateCodeFilter,UsernamePasswordAuthenticationFilter.class)  //添加验证码验证
                .addFilterBefore(smsCodeFilter ,UsernamePasswordAuthenticationFilter.class)  //添加短信验证码
                .formLogin()  //表单方式
                .loginPage("/authentication/require") //指定登录页面
                .loginProcessingUrl("/login")  //指定登录路径
                .successHandler(myAuthenticationSucessHandler())
                .failureHandler(myAuthenticationFailureHandler())
                .and()
                .logout()
                //.logoutUrl("/logout/success")
                .logoutSuccessHandler(logOutSuccessHandler())
                .deleteCookies("JSESSIONID")
                .and()
                .rememberMe() //开启记住我
                .tokenRepository(repository())  //配置持久化token
                .userDetailsService(userDetailsService) //处理自动登录
                .tokenValiditySeconds(3600)  //token持久化失效时间
                .and()
                .authorizeRequests()//授权配置
                .antMatchers("/authentication/require","/login.html","/code/img","/css/login.css",
                        "/loginm.html","/code/sms","/session/invalid").permitAll()
                .anyRequest() //所有请求
                .authenticated()//全部认证
                .and()
                .sessionManagement()  //session会话管理器
                .invalidSessionUrl("/session/invalid")  //过期跳转地址
                .maximumSessions(1)
                .maxSessionsPreventsLogin(true)  //同一账号只允许一台设备登录
                .expiredSessionStrategy(sessionExpiredStrategy())
                .and()
                .and()
                .csrf().disable()
                .exceptionHandling()
                .accessDeniedHandler(accessDeniedHandler())
                .and()
                .apply(smsAuthenticationConfig);
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    @Bean
    public MyAuthenticationSucessHandler myAuthenticationSucessHandler(){
        return  new MyAuthenticationSucessHandler();
    }
    public MyAuthenticationFailureHandler myAuthenticationFailureHandler(){
        return  new MyAuthenticationFailureHandler();
    }
    @Bean
    public MySessionExpiredStrategy sessionExpiredStrategy(){
        return new MySessionExpiredStrategy();
    }

    @Bean
    public MyLogOutSuccessHandler logOutSuccessHandler(){
        return new MyLogOutSuccessHandler();
    }

    @Bean
    public MyAuthenticationAccessDeniedHandler accessDeniedHandler(){
        return new MyAuthenticationAccessDeniedHandler();
    }


}
